from pymodbus.server.asynchronous import ModbusTcpProtocol, ModbusServerFactory
from pymodbus.utilities import hexlify_packets
from twisted.internet import threads
import threading
from twisted.internet.defer import DeferredLock
from database import Mysql
from dcs_message import *
from utils import *

import logging

_logger = logging.getLogger(__name__)


class DcsProtocol(ModbusTcpProtocol):

    def __init__(self):
        #self.lockAddClients = threading.RLock()
        self.lockBuffer = DeferredLock()

    def parseRegisterData(self, data, index=0):
        """ 解析 读取的 PLC寄存器 状态数据,
        从原始的 字节流 解析成 每个纱锭编号 对应的状态值

        寄存器值 2bytes 为一组， 纱锭编号与其状态值 对应顺序为 从右到左， 如下所示
        4号锭  3号锭  2号锭 1号锭
        0011  0001  0000  0001

        具体要看每个工厂的锭号模式

        纱锭状态编码：
        每4个位代表一锭，一个寄存器代表4锭：
        0000 正常，0001断头，0010弱捻，0011空置
        0100 故障，0101旁路
        最大支持2500锭

        @param data 纱锭寄存器值
        @param index 指定这一批寄存器值 对应纱锭 的 起始编号
        """

        length = len(data)
        tail = 1 if length % 2 > 0 else 0
        loop_times = int(length / 2) + tail
        spindles_status = []

        # 每循环一次，处理2个字节， 即一个寄存器的值，得到4个纱锭的状态
        for time in range(0, loop_times):

            # 第 1 个纱锭的状态值       纱锭编号    纱锭状态值
            spindles_status.append((str(index + 0), data[time*2 + 1] & 0x0f))

            # 第 2 个纱锭的状态值
            spindles_status.append((str(index + 1), data[time*2 + 1] & 0xf0))

            # 第 3 个纱锭的状态值
            spindles_status.append((str(index + 2), data[time*2] & 0x0f))

            # 第 4 个纱锭的状态值
            spindles_status.append((str(index + 3), data[time*2] & 0xf0))

            # 下一个循环中纱锭的编号
            index += 4

        return spindles_status

    def dataReceived(self, data):

        if _logger.isEnabledFor(logging.DEBUG):
            _logger.debug('***** DcsProtocol Data Received: %s  ***** ' % hexlify_packets(data))

        if not self.factory.control.ListenOnly:
            units = self.factory.store.slaves()
            single = self.factory.store.single

            # TODO 收到数据的处理流程：
            # 1 解析数据包的完整性， 若不完整则暂且只保存在缓存
            # 2 解析 读取的 PLC寄存器 状态数据，
            # 3 把解析后的 PLC寄存器状态数据 交给后台线程， 后台线程存入数据库
            self.framer.processIncomingPacket(data, self._execute,
                                              single=single,
                                              unit=units)
            print_threading_info()

    def connectionMade(self):

        _logger.debug("Client Connected [%s]" % self.transport.getHost())
        self.framer = self.factory.framer(decoder=self.factory.decoder,
                                          client=None)

        # 连接建立后， 把本连接保存到 factory 队列里, 以便后台线程 每隔 5s 读取一遍寄存器
        self.factory.clients.append(self)
        print(" self.factory.clients :", len(self.factory.clients))

        print_threading_info()

    def connectionLost(self, reason):
        """ Callback for when a client disconnects
        :param reason: The client's reason for disconnecting
        """

        # 连接断开后，把本连接从 factory 队列里 移除
        self.factory.clients.remove(self)
        _logger.debug("Client Disconnected: %s" % reason)

    def _execute(self, result):
        """ Server 端主动读取 PLC单片机 寄存器数据， 收到寄存器数据后无需再返回数据 """
        peer = self.transport.getPeer()
        _logger.debug(peer)

        # TODO 此处尚未传入 纱锭 起始编号， 需要得到机器总计有多少 纱锭，
        # TODO 然后循环 1~5 次才能得到全部纱锭状态值
        spindles_status = self.parseRegisterData(result)

        Mysql.save_in_mysql({"spindles_status": spindles_status, "machine_ip": peer.host})

    def _send(self, message):
        """ Send a request (string) to the network
        :param message: The unencoded modbus response
        """
        if message.should_respond:
            self.factory.control.Counter.BusMessage += 1
            pdu = self.framer.buildPacket(message)
            if _logger.isEnabledFor(logging.DEBUG):
                _logger.debug('send: %s' % b2a_hex(pdu))
            return self.transport.write(pdu)

    def collecting_machine_status(self):
        """ 1 采集机台状态数据 """
        self.send_register_read_request(DcsReadMacStatusRegister)

    def collecting_spinning_status(self):
        """ 2 收集纱锭状态数据 """

        # TODO 1  TCP 粘包问题
        # TODO 2
        self.send_register_read_request(DcsReadSpindleStatusRegister)

    def collecting_spinning_speed(self):
        """ 3 收集纱锭速度 """
        self.send_register_read_request(DcsReadSpindleSpeedRegister)

    def collecting_config_data(self):
        """ 4 收集 机台配置 """
        self.send_register_read_request(DcsReadConfigRegister)

    def send_register_read_request(self, Request):

        # 构造TCP请求帧数据
        request = Request()
        data = request.buildSendingData()

        _logger.debug("*** send_register_read_request ***")
        _logger.debug(data)

        # 发送 读取寄存器的 请求帧数据
        self.transport.write(data)
